package seekting.demo2017.activity.util;

import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.Executor;

import seekting.demo2017.AppEnv;

/**
 * Created by seekting on 17-5-23.
 */

public class BitmapSaver {

    public static final boolean DEBUG = AppEnv.bAppdebug;
    public static final String TAG = "BitmapSaver";
    private Bitmap mBitmap;
    private Executor mExecutor;
    private Handler mUiHandler;
    private Bitmap.CompressFormat mCompressFormat;
    private int mQuality;
    private BitmapSaverCallBack mBitmapSaverCallBack;
    private String mPath;
    public static final int BYTE_ARRAY_PROGRESS = 20;

    public BitmapSaver(Bitmap bitmap, BitmapSaverCallBack bitmapSaverCallBack, Executor executor) {
        mBitmap = bitmap;
        mBitmapSaverCallBack = bitmapSaverCallBack;
        mExecutor = executor;
    }


    public void execute(Bitmap.CompressFormat compressFormat, int quality, File f) {
        execute(compressFormat, quality, f.getAbsolutePath());
    }

    public void execute(Bitmap.CompressFormat compressFormat, int quality, String path) {

        this.mPath = path;
        mQuality = quality;
        mCompressFormat = compressFormat;
        mUiHandler = new Handler(Looper.getMainLooper());

        Runnable runnable = new Runnable() {
            int mFileLength = 0;
            int mCurrentProgress;

            @Override
            public void run() {
//                step1.生成一张小图

                notifySaveBegin();
                OutputStreamProxy out = new OutputStreamProxy(new OnWriteListener() {
                    @Override
                    public void onWrite(int count) {
                        mFileLength = count;
                        mCurrentProgress++;
                        notifyProgress(Math.min(BYTE_ARRAY_PROGRESS, mCurrentProgress));
                        if (DEBUG) {
                            Log.d(TAG, "OutputStreamProxy.onWrite." + count);
                        }

                    }
                });
                mBitmap.compress(mCompressFormat, mQuality, out);
                notifyProgress(BYTE_ARRAY_PROGRESS);
                File file = new File(mPath);
                if (file.exists()) {
                    file.delete();
                }

                try {
                    FileOutStreamProxy fileOutputStream = new FileOutStreamProxy(file, new OnWriteListener() {
                        @Override
                        public void onWrite(int count) {
                            if (DEBUG) {
                                Log.d(TAG, "FileOutStreamProxy.onWrite." + count);
                            }
                            float fcount = count;
                            float progress = BYTE_ARRAY_PROGRESS + (100 - BYTE_ARRAY_PROGRESS) * fcount / mFileLength;
                            notifyProgress(Math.min(Math.round(progress), 99));

                        }
                    });
                    boolean suc = mBitmap.compress(mCompressFormat, mQuality, fileOutputStream);
                    if (suc) {
                        notifyProgress(100);
                        notifySaveEnd();

                    } else {
                        notifySaveError(new RuntimeException("compress return false"));
                    }

                } catch (Throwable e) {
                    notifySaveError(e);
                }


            }
        };
        if (mExecutor != null) {

            mExecutor.execute(runnable);
        } else {
            new Thread(runnable).start();
        }

    }

    private void notifySaveBegin() {
        if (mBitmapSaverCallBack != null) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mBitmapSaverCallBack.onSaveBegin();
                }
            });

        }
    }

    private void notifySaveEnd() {
        if (mBitmapSaverCallBack != null) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mBitmapSaverCallBack.onSaveEnd();
                }
            });

        }
    }

    private void notifyProgress(final int progress) {
        if (mBitmapSaverCallBack != null) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mBitmapSaverCallBack.onSaveProgress(progress);
                }
            });

        }
    }

    private void notifySaveError(final Throwable t) {
        if (mBitmapSaverCallBack != null) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mBitmapSaverCallBack.onSaveError(t);
                }
            });

        }
    }

    private void runOnUiThread(Runnable runnable) {
        mUiHandler.post(runnable);

    }

    public interface BitmapSaverCallBack {

        void onSaveBegin();

        /**
         * @param progress 0-100;
         */
        void onSaveProgress(int progress);

        void onSaveEnd();

        void onSaveError(Throwable t);
    }


    private static class OutputStreamProxy extends OutputStream {
        private OnWriteListener mOnWriteListener;


        int count = 0;

        public OutputStreamProxy(OnWriteListener onWriteListener) {
            super();
            this.mOnWriteListener = onWriteListener;
        }

        @Override
        public void write(byte[] buffer) throws IOException {
            count += buffer.length;
            mOnWriteListener.onWrite(count);
        }

        @Override
        public void write(byte[] buffer, int offset, int length) throws IOException {
            count += length;
            mOnWriteListener.onWrite(count);
        }


        @Override
        public void write(int oneByte) throws IOException {
            count++;
            mOnWriteListener.onWrite(count);
        }
    }

    interface OnWriteListener {
        void onWrite(int count);
    }

    private static class FileOutStreamProxy extends FileOutputStream {
        private OnWriteListener mOnWriteListener;


        int count = 0;

        public FileOutStreamProxy(File file, OnWriteListener onWriteListener) throws FileNotFoundException {
            super(file);
            this.mOnWriteListener = onWriteListener;
        }

        @Override
        public void write(byte[] buffer) throws IOException {
            super.write(buffer);
            //父类还是会走write(byte[] buffer, int byteOffset, int byteCount)方法所以不用count+
        }

        @Override
        public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
            super.write(buffer, byteOffset, byteCount);
            count += byteCount;
            if (mOnWriteListener != null) {
                mOnWriteListener.onWrite(count);
            }
        }

        @Override
        public void write(int oneByte) throws IOException {
            super.write(oneByte);
            //父类还是会走write(byte[] buffer, int byteOffset, int byteCount)方法所以不用+1
        }
    }
}
